package com.example.springstack.springsecurity.config;

import com.example.springstack.springsecurity.Handler.MyAccessDeniedHandler;
import com.example.springstack.springsecurity.Handler.MyAuthenticationException;
import com.example.springstack.springsecurity.Handler.MyAuthenticationFailureHandler;
import com.example.springstack.springsecurity.auth.JwtAuthorizationTokenFilter;
import com.example.springstack.springsecurity.service.impl.UserServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;


/**
 * @author Qiangfanghao
 * <p>
 * WebSecurityConfigurerAdapter:
 * springSecurity config配置参考
 */
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * springSecurity 加密方案 需要先注入bean
     *
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Autowired
    private UserServiceImpl userService;

    /**
     * 自定义基于JWT的安全过滤器
     */
    @Resource
    private JwtAuthorizationTokenFilter authenticationTokenFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //表单登录
        http.formLogin()
                //配置登录页面
                .loginPage("/login.html")
                //自定义登录逻辑请求路径
//                .loginProcessingUrl("/v5/login")
                //登录成功后跳转页面 必须是post请求
                .successForwardUrl("/loginMain")
                .usernameParameter("username")
                .passwordParameter("password")
                //自定义登录成功handler 前后端分离 前端跳转登录成功页面
//                .successHandler(new MyAuthenticationSuccessHandler("http://www.baidu.com"))
                //登录失败后跳转页面 必须是post请求
//                .failureForwardUrl("/loginFailure")
                //自定义登录失败handler 前后端分离 前端跳转登录失败页面
                .failureHandler(new MyAuthenticationFailureHandler("/loginError.html"));

        /**
         * 匹配规则
         * antMatchers():匹配url ant表达式 匹配规则 ? : 匹配一个字符, * : 匹配0或多个字符,** : 匹配0或多个目录
         * anyRequest():所有请求
         * regexMatchers():正则表达式
         *
         * 访问控制:
         * permitAll():放行
         * denyAll():拒绝所有
         *
         * 记住登录权限可以 访问
         * rememberMe():
         * 必须用户名密码登录
         * fullyAuthenticated():
         *
         * anonymous(): 匿名访问
         * authenticated():必须认证
         *
         * 权限匹配
         * hasAuthority():是否有这个权限 严格区分大小写
         * hasAnyAuthority():匹配多个权限 严格区分大小写
         *
         * 角色匹配: 角色定义 必须是 ROLE_ 开头  security匹配时候不能以 ROLE_ 开头 源码会自动拼接
         * hasRole(): 根据定义角色进行匹配 严格区分大小写
         * hasAnyRole: 匹配多个角色
         *
         * IP匹配
         * hasIpAddress() 后台管理系统
         *
         * access 重点: @prePostEnabled 注解约等于 access
         * access 主要功能:
         *  1.可以自定义权限
         *  2.可以调用 springSecurity中的 所有角色权限控制 方法
         */

        //授权管理
        http.authorizeRequests()
                //放行登录页面
                .antMatchers("/login.html").permitAll()
                //放行登录失败页面
                .antMatchers("/loginError.html").permitAll()
                //放行登录接口
                .antMatchers(HttpMethod.POST,"/test/login").permitAll()
//                .antMatchers("login.html").hasAuthority("admin")
//                .antMatchers("login.html").hasAnyAuthority("admin")
//                .antMatchers("login.html").hasAnyRole("admin")
//                .antMatchers("/main.html").hasIpAddress("172.16.5.65")
                //access 自定义判断url权限 anyRequest只能有一个认证方式
//                .anyRequest().access("@myAccess.hasPermission(httpServletRequest,authentication)")
                //所有请求必须认证后才能登陆
                //放行 oauth 认证请求
                .antMatchers("/oauth/**").permitAll()
                .anyRequest().authenticated()
                .and().headers().frameOptions().disable();



        //自定义退出登录
        http.logout()
                //退出登录连接
                .logoutUrl("/logout")
                //退出登录成功跳转url
                .logoutSuccessUrl("/logout.html");

        //自定义 异常处理 403页面
        http.exceptionHandling()
                //处理 access异常
                .accessDeniedHandler(new MyAccessDeniedHandler())
                //处理捕获 认证异常
                .authenticationEntryPoint(new MyAuthenticationException());

        //关闭csrf防火墙 否则会拦截 /login请求
        http.csrf().disable();

        //自定义 jwt用户登录认证
        http.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);


    }



    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService);
    }
}
